package web.stu.controller;

import cn.hutool.core.util.ObjUtil;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;
import web.stu.common.ErrorCode;
import web.stu.config.Result;
import web.stu.exception.BusinessException;
import web.stu.model.dto.score.AddScoreDTO;
import web.stu.model.dto.score.QueryScoreDTO;
import web.stu.model.dto.score.UpdateScoreDTO;
import web.stu.model.entity.User;
import web.stu.model.enums.UserRoleEnum;
import web.stu.model.vo.ScoreVO;
import web.stu.service.ScoreService;
import web.stu.service.UserService;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.List;

/**
 * 成绩管理控制器
 * <p>
 * 提供成绩相关的REST API，包括：
 * - 学生查看成绩
 * - 教师录入、修改、删除成绩
 * - 管理员审核异常成绩
 * - 按学生、课程查询成绩
 * - 分页查询成绩列表
 * </p>
 */
@Slf4j
@RestController
@RequestMapping("/score")
@Api(value = "分数的管理模块，记录分数", tags = "分数模块")
public class ScoreController {

    @Resource
    private ScoreService scoreService;

    @Resource
    private UserService userService;

    /**
     * 学生端查看成绩列表接口
     * @param queryScoreDTO 查询条件，包含分页参数、课程ID等筛选条件
     * @param request HTTP请求对象，用于获取会话信息
     * @return 包含分页成绩信息的Result对象
     * @throws BusinessException 如果查询参数为空或用户未登录会抛出异常
     * @apiNote 此接口仅供学生查看自己的成绩，会自动根据当前登录学生的ID进行过滤。
     *          返回的成绩信息包含：课程名称、成绩、学分、教师姓名等
     */
    @PostMapping("/list/student")
    @ApiOperation(value = "listScoresToStudent", notes = "学生端展示分数")
    public Result<Page<ScoreVO>> listScoresToStudent(@RequestBody QueryScoreDTO queryScoreDTO, HttpServletRequest request) {
        // 非空判断
        if (ObjUtil.isEmpty(queryScoreDTO)) {
            throw new BusinessException(ErrorCode.PRIMARY_ERROR);
        }
        Page<ScoreVO> scores = scoreService.listAllScoresToStudent(queryScoreDTO, request);
        return Result.success(scores);
    }

    /**
     * 教师端查看成绩列表接口
     * @param queryScoreDTO 查询条件，包含分页参数、课程ID、学生ID等筛选条件
     * @param request HTTP请求对象，用于获取会话信息
     * @return 包含分页成绩信息的Result对象
     * @throws BusinessException 如果查询参数为空、用户未登录或权限不足会抛出异常
     * @apiNote 此接口仅供教师和管理员使用，教师只能查看自己教授课程的学生成绩。
     *          支持按课程ID、学生ID、班级ID等条件筛选。
     *          已知问题：模糊匹配功能存在一些问题，需要优化
     */
    @PostMapping("/list/teacher")
    @ApiOperation(value = "listScoresToTeacher", notes = "教师端展示分数")
    public Result<Page<ScoreVO>> listScoresToTeacher(@RequestBody QueryScoreDTO queryScoreDTO, HttpServletRequest request) {
        // 非空判断
        if (ObjUtil.isEmpty(queryScoreDTO)) {
            throw new BusinessException(ErrorCode.PRIMARY_ERROR);
        }
        // 权限校验
        User loginUser = userService.getLoginUser(request);
        if (!userService.isCompareRole(loginUser, UserRoleEnum.TEACHER.getKey())
                && !userService.isCompareRole(loginUser, UserRoleEnum.ADMIN.getKey())) {
            throw new BusinessException(ErrorCode.NO_AUTH_ERROR, "仅教师或管理员可用");
        }
        // 教师条件查询
        Page<ScoreVO> scoreVOS = scoreService.listScoresToTeacher(queryScoreDTO, request);
        return Result.success(scoreVOS);
    }


    /**
     * 教师录入学生成绩接口
     * @param addScoreDTO 成绩信息，包含学生ID、课程ID、成绩分数等
     * @param request HTTP请求对象，用于获取会话信息
     * @return 新增成绩记录的ID
     * @throws BusinessException 如果用户未登录、不是教师角色、或成绩信息不完整会抛出异常
     * @apiNote 此接口仅供教师使用，用于为学生录入课程成绩。
     *          教师只能为自己教授的课程录入成绩。
     *          成绩录入后会自动记录操作教师信息和录入时间。
     */
    @PostMapping("/add")
    @ApiOperation(value = "addScore", notes = "教师端录入分数")
    public Result<Integer> addScore(@RequestBody AddScoreDTO addScoreDTO, HttpServletRequest request) {
        // 获取当前权限
        User loginUser = userService.getLoginUser(request);
        if (!userService.isCompareRole(loginUser, UserRoleEnum.TEACHER.getKey())) {
            throw new BusinessException(ErrorCode.NO_AUTH_ERROR, "只有教师能够录入成绩");
        }

        Integer scoreId = scoreService.addScore(addScoreDTO, request);
        return Result.success(scoreId);
    }


    /**
     * 修改学生成绩接口
     * @param updateScoreDTO 更新的成绩信息，包含成绩ID、新的分数等
     * @param request HTTP请求对象，用于获取会话信息
     * @return 空Result对象表示操作成功
     * @throws BusinessException 如果用户未登录、权限不足、成绩不存在会抛出异常
     * @apiNote 此接口供教师和管理员使用，用于修改已录入的成绩。
     *          教师只能修改自己教授课程的成绩。
     *          管理员可以修改任何课程的成绩。
     *          修改成绩时会自动记录修改人和修改时间。
     */
    @PutMapping("/update")
    @ApiOperation(value = "updateScore", notes = "教师端修改分数")
    public Result<Void> updateScore(@RequestBody UpdateScoreDTO updateScoreDTO, HttpServletRequest request) {
        // 获取当前权限
        User loginUser = userService.getLoginUser(request);
        if (!userService.isCompareRole(loginUser, UserRoleEnum.TEACHER.getKey())
                && !userService.isCompareRole(loginUser, UserRoleEnum.ADMIN.getKey())) {
            throw new BusinessException(ErrorCode.NO_AUTH_ERROR, "仅教师或管理员可用");
        }
        scoreService.updateScore(updateScoreDTO, request);
        return Result.success();
    }

    /**
     * 删除成绩记录接口
     * @param id 要删除的成绩记录ID
     * @param request HTTP请求对象，用于获取会话信息
     * @return 空Result对象表示操作成功
     * @throws BusinessException 如果用户未登录、不是教师角色、成绩不存在会抛出异常
     * @apiNote 此接口仅供教师使用，用于删除错误录入的成绩记录。
     *          教师只能删除自己教授课程的成绩。
     *          删除操作会被记录在系统日志中。
     */
    @DeleteMapping("/delete/{id}")
    @ApiOperation(value = "deleteScore", notes = "教师删除分数")
    public Result<Void> deleteScore(@PathVariable Integer id, HttpServletRequest request) {
        // 获取当前权限
        User loginUser = userService.getLoginUser(request);
        if (!userService.isCompareRole(loginUser, UserRoleEnum.TEACHER.getKey())) {
            throw new BusinessException(ErrorCode.NO_AUTH_ERROR, "只有教师能够删除成绩");
        }
        scoreService.deleteScore(id);
        return Result.success();
    }

    /**
     * 根据ID查询成绩详情接口
     * @param id 成绩记录ID
     * @return 包含成绩详细信息的Result对象
     * @throws BusinessException 如果成绩ID不存在会抛出异常
     * @apiNote 此接口用于查询单条成绩记录的详细信息，包括课程名称、学生姓名、成绩分数、
     *          录入教师、录入时间等。任何角色都可以调用，但学生只能查看自己的成绩。
     */
    @GetMapping("/{id}")
    @ApiOperation(value = "getScoreById", notes = "根据分数id查询分数")
    public Result<ScoreVO> getScoreById(@PathVariable Integer id) {
        ScoreVO scoreVO = scoreService.getScoreById(id);
        return Result.success(scoreVO);
    }

    /**
     * 查询学生所有成绩接口
     * @param studentId 学生ID
     * @return 包含该学生所有成绩信息的Result对象
     * @throws BusinessException 如果学生ID不存在会抛出异常
     * @apiNote 此接口用于查询指定学生的所有课程成绩。
     *          返回的成绩列表按照课程时间倒序排列。
     *          包含每门课程的详细信息（课程名称、学分、教师等）。
     *          学生只能查询自己的成绩，教师可以查询自己教授课程的学生成绩，
     *          管理员可以查询所有学生的成绩。
     */
    @GetMapping("/student/{studentId}")
    @ApiOperation(value = "getScoresByStudent", notes = "根据学生id获取分数")
    public Result<List<ScoreVO>> getScoresByStudent(@PathVariable Integer studentId) {
        List<ScoreVO> scores = scoreService.getScoresByStudent(studentId);
        return Result.success(scores);
    }

    /**
     * 查询课程所有学生成绩接口
     * @param courseId 课程ID
     * @return 包含该课程所有学生成绩信息的Result对象
     * @throws BusinessException 如果课程ID不存在会抛出异常
     * @apiNote 此接口用于查询指定课程的所有学生成绩。
     *          返回的成绩列表按照学生学号排序。
     *          包含每个学生的详细信息（学号、姓名、班级等）和成绩。
     *          教师可以查询自己教授课程的成绩，
     *          管理员可以查询所有课程的成绩。
     */
    @GetMapping("/course/{courseId}")
    @ApiOperation(value = "getScoresByCourse", notes = "根据课程id获取分数")
    public Result<List<ScoreVO>> getScoresByCourse(@PathVariable Integer courseId) {
        List<ScoreVO> scores = scoreService.getScoresByCourse(courseId);
        return Result.success(scores);
    }

    /**
     * 管理员标记异常成绩接口
     * @param updateScoreDTO 包含成绩ID和异常标记信息的DTO
     * @param request HTTP请求对象，用于获取会话信息
     * @return 空Result对象表示操作成功
     * @throws BusinessException 如果参数为空、用户非管理员或成绩不存在会抛出异常
     * @apiNote 此接口仅供管理员使用，用于标记可能存在问题的成绩记录。
     *          标记异常成绩后，系统会自动通知相关教师进行确认或修改。
     *          异常标记会记录操作管理员和标记原因。
     */
    @PostMapping("/update/mark")
    @ApiOperation(value = "markExceptionByAdmin", notes = "管理员标记分数异常")
    public Result<Void> markExceptionByAdmin(@RequestBody UpdateScoreDTO updateScoreDTO, HttpServletRequest request) {
        // 非空判断
        if (ObjUtil.isEmpty(updateScoreDTO)) {
            throw new BusinessException(ErrorCode.PRIMARY_ERROR);
        }
        // 只能管理员执行
        User loginUser = userService.getLoginUser(request);
        if (!userService.isCompareRole(loginUser, UserRoleEnum.ADMIN.getKey())) {
            throw new BusinessException(ErrorCode.NO_AUTH_ERROR, "只能管理员操作");
        }
        scoreService.updateScoreByAdminAudit(updateScoreDTO, request);
        return Result.success();
    }
}

